tree grammar pascalWalker;

//class PascalTreeParserSuper extends TreeParser;

options {
	tokenVocab=pascal; // importuj tokeny z pascal.g
	ASTLabelType=CommonTree;
	output=template;
}

scope PScope {
	String name;
	List<Symbol> symbols;
	int liczbaParametrow;
	int constCount;
	int varCount;
	int ifCount;
	int whileCount;
	int repeatCount;
	int caseCount;
	int forCount;
	int ltCount;
	int leCount;
	int gtCount;
	int geCount;
	int eqCount;
	int neCount;
}

@header {
//package org.eu.ravn.pascal;
import java.util.LinkedList;
}

@members {
	class Symbol {
		public Symbol(String name, String type) {
			this.name=name;
			this.type=type;
			this.count=0;
			this.retval=0;
		}
		public Symbol(String name, String type, int count) {
			this.name=name;
			this.type=type;
			this.count=count;
			this.retval=0;
		}
		public Symbol(String name, String type, int count, int retval) {
			this.name=name;
			this.type=type;
			this.count=count;
			this.retval=retval;
		}
		public Symbol(String name, String type, int count, int retval,boolean glowny) {
			this.name=name;
			this.type=type;
			this.count=count;
			this.retval=retval;
			this.glowny=glowny;
		}
		public int retval;
		public String name;
		public String type;
		public int count;
		public boolean glowny;
		public boolean equals(Symbol s2) {
			return this.name.equals(s2.name) && this.type.equals(s2.type);
		}
	};

	boolean isDefined(Symbol id) {
		/*if(id.name.toLowerCase().equals("writeln") && id.type.equals("procedura")) {
			return true;
		}
		if(id.name.toLowerCase().equals("write") && id.type.equals("procedura")) {
			return true;
		}
		if(id.name.toLowerCase().equals("readln") && id.type.equals("procedura")) {
			return true;
		}
		if(id.name.toLowerCase().equals("read") && id.type.equals("procedura")) {
			return true;
		}*/
		List<String> proceduryWbudowane=new LinkedList<String>();
		proceduryWbudowane.add("writeln");
		//proceduryWbudowane.add("write");
		//proceduryWbudowane.add("readln");
		//proceduryWbudowane.add("read");
		if(proceduryWbudowane.contains(id.name.toLowerCase()))
			return true;
		for (int s=$PScope.size()-1; s>=0; s--) {
			for (Symbol symbol : $PScope[s]::symbols) {
				if(symbol.name.equals(id.name)) {
					//System.out.println("Znaleziono");
					return true;
				}
			}
		}
		return false;
	}
	
	int najwiekszy(int v1,int v2) {
		if(v1>=v2)
			return v1;
		return v2;
	}
	
	Symbol getSymbol(String name, String type) {
		for (int s=$PScope.size()-1; s>=0; s--) {
			for (Symbol symbol : $PScope[s]::symbols) {
				if(symbol.name.equals(name)) {
					//System.out.println("Znaleziono");
					return symbol;
				}
			}
		}
		return null;
	}
	
	Symbol getSymbol(Symbol id) {
		return getSymbol(id.name,id.type);
	}
  private static String programName;
}


program
scope PScope;
@init {
	$PScope::symbols = new ArrayList<Symbol>();
	$PScope::name = "global";
}
    : np=naglowek_programu
      b=blok[true]
       -> program(naglowek={$np.st},blok={$b.st})
    ;

naglowek_programu
    : ^(PROGRAM IDENT lista_identyfikatorow[0]?) { programName=$IDENT.text; } -> naglowek(id={$IDENT.text},ids={$lista_identyfikatorow.st})
    ;

identyfikator
    : IDENT
    ;

blok[boolean glowny] returns [int stack,int locals]
scope PScope;
@init {
	$PScope::symbols = new ArrayList<Symbol>();
	$PScope::name = "blok";
	int constCount = 0;
	int varCount = 0;
	int ifCount = 0;
	int whileCount = 0;
	int repeatCount = 0;
	int caseCount = 0;
	int forCount = 0;
	int ltCount = 0;
	int leCount = 0;
	int gtCount = 0;
	int geCount = 0;
	int eqCount = 0;
	int neCount = 0;
}
    : ( e=czesc_deklarujaca_etykiety[glowny]
      | s=czesc_definiujaca_stale[glowny]
      | t=czesc_definiujaca_typy[glowny]
      | z=czesc_deklarujaca_zmienne[glowny]
      | o=czesc_deklarujaca_procedury_i_funkcje[glowny]
      )*
      i=instrukcja_zlozona
      { $stack=$i.liczbaInstrukcji; $locals=$z.count+$s.count; }
      ( {$glowny}? -> blok_glowny(etykiety={$e.st}, stale={$s.st}, typy={$t.st}, zmienne={$z.st}, operacje={$o.st}, instrukcje={$i.st},stack={$i.liczbaInstrukcji},varnum={$PScope::varCount+$PScope::constCount+1}) 
      | {!$glowny}? -> blok(etykiety={$e.st}, stale={$s.st}, typy={$t.st}, zmienne={$z.st}, operacje={$o.st}, instrukcje={$i.st},stack={$i.liczbaInstrukcji},varnum={$PScope::varCount+$PScope::constCount}) )
    ;

czesc_deklarujaca_etykiety[boolean glowny]
    : ^(LABEL e+=etykieta+) -> etykiety(ets={$e})
    ;

etykieta
    : NUM_INT	{
    		//System.out.println("etykieta: "+$NUM_INT.text);
    		Symbol newSymbol=new Symbol($NUM_INT.text,"etykieta");
    		if($PScope::symbols.contains(newSymbol)) {
    			System.err.println("Etykieta "+newSymbol.name+" zostala juz wczesniej zdeklarowana.");
    			System.exit(1);
    		}
    		$PScope::symbols.add(newSymbol);
    } -> przekaz(item={$NUM_INT.text})
    ;

czesc_definiujaca_stale[boolean glowny] returns [int count]
    : ^(CONST consts+=definicja_stalej[glowny]+ ) { $count=$consts.size(); }-> stale(consts={$consts})
    ;

definicja_stalej[boolean glowny]
    : ^(ROWNOSC IDENT stala) {
    		//System.out.println("stala: "+$IDENT.text);
    		Symbol newSymbol=new Symbol($IDENT.text,"stala",$PScope::constCount,$stala.typ,glowny);
    		if($PScope::symbols.contains(newSymbol)) {
    			System.err.println("Stala "+newSymbol.name+" zostala juz wczesniej zdeklarowana.");
    			System.exit(1);
    		}
    		$PScope::symbols.add(newSymbol);
    		$PScope::constCount++;
    	} ( {$glowny}? -> stala_glowny(id={$IDENT.text},value={$stala.constant},string={$stala.typ==3 ? "str" : null},float={$stala.typ==2 ? "float" : null}) 
      | {!$glowny}? -> stala(id={$IDENT.text},value={$stala.constant},string={$stala.typ==3 ? "str" : null},float={$stala.typ==2 ? "float" : null}) )
    ;

stala returns [String constant, boolean isIdent, int typ]
    : NUM_INT { $constant=$NUM_INT.text; $isIdent=false; $typ=1; } -> przekaz(item={$NUM_INT.text})
    | NUM_REAL { $constant=$NUM_REAL.text; $isIdent=false; $typ=2; } -> przekaz(item={$NUM_REAL.text})
    | ^( PLUS
         val=( NUM_INT
         | NUM_REAL
         | IDENT
         )
       ) { $constant=$val.text; $isIdent=false; $typ=1; } -> przekaz(item={$val.text})
    | ^( MINUS
         val=( NUM_INT
         | NUM_REAL
         | IDENT
         )
       ) { $constant="-".concat($val.text); $isIdent=false; $typ=1; }  -> przekaz(item={"-".concat($val.text)})
    | IDENT { $constant=$IDENT.text; $isIdent=true; } -> przekaz(item={$IDENT.text})
    | STRING_LITERAL { $constant=$STRING_LITERAL.text; $isIdent=false; $typ=3; } -> przekaz(item={$STRING_LITERAL.text.substring(1,$STRING_LITERAL.text.length()-1)})
    ;

napis
    : STRING_LITERAL -> napis(text={$STRING_LITERAL.text.substring(1,$STRING_LITERAL.text.length()-1)})
    ;

czesc_definiujaca_typy[boolean glowny]
    : ^(TYPE types+=definicja_typu+) { 
    	if(!$glowny) {
    		System.out.println("Nie mozna definiowac typu w zagniezdzonych blokach (np. w procedurach)");
    		System.exit(1);
    	} } -> typy(types={$types})
    ;

definicja_typu
    : ^(TYPEDECL IDENT nowy_typ) {
    		//System.out.println("typ: "+$IDENT.text);
    		Symbol newSymbol=new Symbol($IDENT.text,"typ");
    		if($PScope::symbols.contains(newSymbol)) {
    			System.err.println("Typ "+newSymbol.name+" zostal juz wczesniej zdefiniowany.");
    			System.exit(1);
    		}
    		$PScope::symbols.add(newSymbol);
    } -> defTypu(id={$IDENT.text},typ={$nowy_typ.st})
    ;

nowy_typ returns [int typ]
    : ^(SCALARTYPE lista_identyfikatorow[0]) {$typ=4;}-> typWyliczeniowy(ids={$lista_identyfikatorow.st})
    | ^(DWIEKROPKI a=stala b=stala) {$typ=1;} -> typOkrojony(start={$a.constant},end={$b.constant})
    | identyfikator_typu {$typ=$identyfikator_typu.typ; }-> przekaz(item={$identyfikator_typu.st})
    | nowy_typ_strukturalny {$typ=5;}-> przekaz(item={$nowy_typ_strukturalny.st})
    | ^(WSKAZNIK identyfikator_typu) {$typ=$identyfikator_typu.typ;} -> przekaz(item={$identyfikator_typu.st})
    ;

identyfikator_typu returns [int typ]
    : IDENT	{ // sprawdzac w tablicy symboli czy nie? 
    	$typ=6;
    } -> przekaz(item={$IDENT.text})
    | CHAR { $typ=7; }-> przekaz(item={"char"})
    | BOOLEAN { $typ=1; } -> przekaz(item={"boolean"})
    | INTEGER { $typ=1; } -> przekaz(item={"int"})
    | REAL { $typ=2; } -> przekaz(item={"float"})
    | ^( STRING
         ( IDENT
         | NUM_INT
         | NUM_REAL
         |
         )
       ) { $typ=3; } -> przekaz(item={"java/lang/String"})
    ;

nowy_typ_strukturalny
    : ^(PACKED typ_strukturalny_niespakowany) -> przekaz(item={$typ_strukturalny_niespakowany.st})
    | typ_strukturalny_niespakowany -> przekaz(item={$typ_strukturalny_niespakowany.st})
    ;

typ_strukturalny_niespakowany
    : typ_tablicowy -> przekaz(item={$typ_tablicowy.st})
    | typ_rekordowy -> przekaz(item={$typ_rekordowy.st})
    ;

typ_tablicowy
    : ^(ARRAY lista_typow_indeksowych nowy_typ) -> typTablicowy(indexes={$lista_typow_indeksowych.st},typ={$nowy_typ.st})
    ;

lista_typow_indeksowych
    : ^( TYPELIST a+=nowy_typ+ ) -> commaIdList(ids={$a})
    ;

typ_rekordowy
    : ^(RECORD lista_pol) -> typRekordowy(pola={$lista_pol.st})
    ;

lista_pol
    : ^( FIELDLIST
         ( a=czesc_stala b=czesc_zmienna?
         | b=czesc_zmienna
         )
       ) -> listaPol(const={$a.st},var={$b.st}) 
    ;

czesc_stala
    : a+=sekcja_rekordu+ -> sekcjeRekordu(el={$a})
    ;

sekcja_rekordu
//    : ^(FIELD lista_identyfikatorow[$nowy_typ.typ] nowy_typ) -> sekcjaRekordu(ids={$lista_identyfikatorow.st},typ={$nowy_typ.st})
      : ^(FIELD nowy_typ lista_identyfikatorow[$nowy_typ.typ]) -> sekcjaRekordu(ids={$lista_identyfikatorow.st},typ={$nowy_typ.st})
    ;

czesc_zmienna
    : ^(CASE a=selektor_wariantu b+=wariant+ ) -> czescZmiennaRekordu(selektor={$a.st},warianty={$b})
    ;

selektor_wariantu
    : ^(VARIANT_TAG identyfikator identyfikator_typu) -> selektorWariantu1(id={$identyfikator.text},typ={$identyfikator_typu.st})
    | ^(VARIANT_TAG_NO_ID identyfikator_typu) -> selektorWariantu2(typ={$identyfikator_typu.st})
    ;

wariant
    : ^(VARIANT_CASE lista_stalych_wyboru lista_pol) -> wariant(consts={$lista_stalych_wyboru.st},pola={$lista_pol.st})
    ;

czesc_deklarujaca_zmienne[boolean glowny] returns [int count]
    : ^( VAR vars+=deklaracja_zmiennej[glowny]+ ) {$count=$vars.size(); } -> zmienne(vars={$vars})
    ;

deklaracja_zmiennej[boolean glowny]
//: ^(VARDECL lista_identyfikatorow[$nowy_typ.typ] nowy_typ)    
: ^(VARDECL nowy_typ lista_identyfikatorow[$nowy_typ.typ])    
    ( {$glowny}? -> zmienna_glowny(ids={$lista_identyfikatorow.a},type={$nowy_typ.st}, int={"int"}) 
      | {!$glowny}? -> zmienna(ids={$lista_identyfikatorow.a},type={$nowy_typ.st}) )
    ;

czesc_deklarujaca_procedury_i_funkcje[boolean glowny]
    : d+=deklaracja_procedury_lub_funkcji[glowny]+ -> operacje(ops={$d})
    ;

deklaracja_procedury_lub_funkcji[boolean glowny]
    : deklaracja_procedury { 
    	if(!$glowny) {
    		System.out.println("Nie mozna definiowac procedury w zagniezdzonych blokach (np. w procedurach)");
    		System.exit(1);
    	} } -> przekaz(item={$deklaracja_procedury.st})
    | deklaracja_funkcji { 
    	if(!$glowny) {
    		System.out.println("Nie mozna definiowac funkcji w zagniezdzonych blokach (np. w procedurach)");
    		System.exit(1);
    	} }-> przekaz(item={$deklaracja_funkcji.st})
    ;

deklaracja_procedury
scope PScope;
@init {
	$PScope::symbols = new ArrayList<Symbol>();
	$PScope::name = "procedura";
	$PScope::liczbaParametrow = 0;
}
    : ^(PROCEDURE IDENT lista_parametrow_formalnych? blok[false]) {
    		//System.out.println("Procedura "+$IDENT.text);
    		int i=$PScope.size()-2;
    		Symbol newSymbol=new Symbol($IDENT.text,"procedura");
    		if($PScope[i]::symbols.contains(newSymbol)) {
    			System.err.println("Procedura "+newSymbol.name+" zostala juz wczesniej zdeklarowana.");
    			System.exit(1);
    		}
    		$PScope[i]::symbols.add(newSymbol);
    } -> procedura(nazwa={$IDENT.text},vars={$lista_parametrow_formalnych.st},blok={$blok.st},stack={$blok.stack},varnum={$blok.locals+$PScope::liczbaParametrow})
    ;

lista_parametrow_formalnych
    : ^(ARGDECLS a+=sekcja_parametrow_formalnych+) -> zmienne(vars={$a})
    ;

sekcja_parametrow_formalnych returns [int count]
    : grupa_parametrow { $count=$grupa_parametrow.count; $PScope::liczbaParametrow+=$grupa_parametrow.count; } -> przekaz(item={$grupa_parametrow.st})
    | ^(VAR grupa_parametrow) { $count=$grupa_parametrow.count; $PScope::liczbaParametrow+=$grupa_parametrow.count; } -> przekaz(item={$grupa_parametrow.st})
    ;

grupa_parametrow returns [int count]
//    : ^(ARGDECL lista_identyfikatorow[$identyfikator_typu.typ] identyfikator_typu) { $count=$lista_identyfikatorow.count; } -> zmienna(ids={$lista_identyfikatorow.st},type={$identyfikator_typu.st})
     : ^(ARGDECL identyfikator_typu lista_identyfikatorow[$identyfikator_typu.typ]) { $count=$lista_identyfikatorow.count; } -> zmienna(ids={$lista_identyfikatorow.st},type={$identyfikator_typu.st})
    ;

lista_identyfikatorow [int typ] returns [List<String> a, int count]
    : ^(IDLIST ids+=identyfikator_w_procedurze[typ]+) { $a=$ids; $count=$ids.size(); } -> commaIdList(ids={$ids})
    ;
    
identyfikator_w_procedurze [int typ] returns [String id]
	:	IDENT {
		//System.out.println("Zmienna: "+$IDENT.text+" count: "+$PScope::varCount);
		Symbol newSymbol=new Symbol($IDENT.text,"zmienna",$PScope::varCount,$typ);
    		if($PScope::symbols.contains(newSymbol)) {
    			System.err.println("Zmienna "+newSymbol.name+" zostala juz wczesniej zdeklarowana.");
    			System.exit(1);
    		}
    		$PScope::symbols.add(newSymbol);
    		$id=$IDENT.text;
    		$PScope::varCount++;
	} -> przekaz(item={$IDENT.text})
	;

lista_stalych_wyboru
    : ^(CONSTLIST a+=stala+) -> listaStalychWyboru(consts={$a})
    ;

deklaracja_funkcji
scope PScope;
@init {
	$PScope::symbols = new ArrayList<Symbol>();
	$PScope::name = "funkcja";
	$PScope::liczbaParametrow = 1;		// identyfikator funkcji jako zmienna przechowujaca wartosc zwracana
}
    : ^(FUNCTION IDENT lista_parametrow_formalnych? typ_wyniku blok[false]) {
    		//System.out.println("Funkcja "+$IDENT.text);
    		int i=$PScope.size()-2;
    		Symbol newSymbol=new Symbol($IDENT.text,"funkcja",$blok.locals+$PScope::liczbaParametrow-1);
    		if($PScope[i]::symbols.contains(newSymbol)) {
    			System.err.println("Funkcja "+newSymbol.name+" zostala juz wczesniej zdeklarowana.");
    			System.exit(1);
    		}
    		$PScope[i]::symbols.add(newSymbol);
    		$PScope::varCount++;
    } -> funkcja(nazwa={$IDENT.text},vars={$lista_parametrow_formalnych.st},blok={$blok.st},wynik={$typ_wyniku.st},stack={$blok.stack},varnum={$blok.locals+$PScope::liczbaParametrow},retnum={$blok.locals+$PScope::liczbaParametrow-1})
    ;

typ_wyniku
    : identyfikator_typu -> przekaz(item={$identyfikator_typu.st})
    ;

instrukcja
    : ^(DWUKROPEK etykieta instrukcja_bez_etykiety) -> instrukcjaLabel(label={$etykieta.st},instr={$instrukcja_bez_etykiety.st})
    | instrukcja_bez_etykiety -> przekaz(item={$instrukcja_bez_etykiety.st})
    ;

instrukcja_bez_etykiety
    : instrukcja_prosta -> przekaz(item={$instrukcja_prosta.st})
    | instrukcja_strukturalna -> przekaz(item={$instrukcja_strukturalna.st})
    ;

instrukcja_prosta
    : instrukcja_przypisania -> przekaz(item={$instrukcja_przypisania.st})
    | instrukcja_procedury -> przekaz(item={$instrukcja_procedury.st})
    | instrukcja_skoku -> przekaz(item={$instrukcja_skoku.st})
    ;

instrukcja_przypisania
    : ^(PRZYPISANIE zmienna wyrazenie) -> przypisanie(var={$zmienna.st},val={$wyrazenie.st},varnum={getSymbol($zmienna.name,"zmienna").count},string={$wyrazenie.typ==3 ? 3 : null},float={$wyrazenie.typ==2 ? 2 : null})
    ;

zmienna returns [String name,int typ]
    : ^(LNAWIASKWADRATOWY a=zmienna wyr+=wyrazenie+) {$name=$a.name;} -> tablica(name={$a.st},indeksy={$wyr})
    | ^(LNAWIAS2 a=zmienna wyr+=wyrazenie+) {$name=$a.name;} -> tablica(name={$a.st},indeksy={$wyr})
    | ^(KROPKA a=zmienna IDENT) {$name=$a.name;} -> poleRekodu(rekord={$a.st},pole={$IDENT.text})
    | ^(WSKAZNIK a=zmienna) {$name=$a.name;} -> wskaznik(name={$a.st})
    | IDENT {
    	Symbol sym0=new Symbol($IDENT.text, "zmienna");
	if ( !isDefined( sym0 ) && !isDefined(new Symbol($IDENT.text, "funkcja")) ) {
		System.err.println("niezdefiniowana zmienna: "+$IDENT.text);
		System.exit(1);
	}
	Symbol sym=getSymbol(sym0);
	String cos=null;
	if(sym.glowny) {
		cos="glowny";
	}
	$name=$IDENT.text;
	$typ=sym.retval;
    } -> variable(name={$IDENT.text},varnum={sym.count},string={$typ==3 ? "str" : null},float={$typ==2 ? "float" : null},glowny={cos})
    ;

wyrazenie returns [int typ]
    : ^(ROWNOSC a=wyrazenie b=wyrazenie) {$typ=1;} -> rownosc(wyr1={$a.st},wyr2={$b.st},num={$PScope::eqCount++},float={$a.typ==2 && $b.typ==2 ? "float" : null})
    | ^(NIEROWNOSC a=wyrazenie b=wyrazenie) {$typ=1;} -> nierownosc(wyr1={$a.st},wyr2={$b.st},num={$PScope::neCount++},float={$a.typ==2 && $b.typ==2 ? "float" : null})
    | ^(MNIEJSZY a=wyrazenie b=wyrazenie) {$typ=1;} -> mniejszy(wyr1={$a.st},wyr2={$b.st},num={$PScope::ltCount++},float={$a.typ==2 && $b.typ==2 ? "float" : null})
    | ^(MNIEJSZYLUBROWNY a=wyrazenie b=wyrazenie) {$typ=1;} -> mniejszylubrowny(wyr1={$a.st},wyr2={$b.st},num={$PScope::leCount++},float={$a.typ==2 && $b.typ==2 ? "float" : null})
    | ^(WIEKSZY a=wyrazenie b=wyrazenie) {$typ=1;} -> wiekszy(wyr1={$a.st},wyr2={$b.st},num={$PScope::gtCount++},float={$a.typ==2 && $b.typ==2 ? "float" : null})
    | ^(WIEKSZYLUBROWNY a=wyrazenie b=wyrazenie) {$typ=1;} -> wiekszylubrowny(wyr1={$a.st},wyr2={$b.st},num={$PScope::geCount++},float={$a.typ==2 && $b.typ==2 ? "float" : null})
    | ^(IN a=wyrazenie b=wyrazenie) {$typ=1;} -> in(wyr1={$a.st},wyr2={$b.st})
    | ^(PLUS a=wyrazenie b=wyrazenie?) {$typ=najwiekszy($a.typ,$b.typ);} -> plus(wyr1={$a.st},wyr2={$b.st},float={$a.typ==2 && $b.typ==2 ? "float" : null})
    | ^(MINUS a=wyrazenie b=wyrazenie?) {$typ=najwiekszy($a.typ,$b.typ);} -> minus(wyr1={$a.st},wyr2={$b.st},float={$a.typ==2 && $b.typ==2 ? "float" : null})
    | ^(OR a=wyrazenie b=wyrazenie) {$typ=1;} -> or(wyr1={$a.st},wyr2={$b.st})
    | ^(GWIAZDKA a=wyrazenie b=wyrazenie) {$typ=najwiekszy($a.typ,$b.typ);} -> gwiazdka(wyr1={$a.st},wyr2={$b.st},float={$a.typ==2 && $b.typ==2 ? "float" : null})
    | ^(SLASH a=wyrazenie b=wyrazenie {$typ=najwiekszy($a.typ,$b.typ);}) -> slash(wyr1={$a.st},wyr2={$b.st},float={$a.typ==2 && $b.typ==2 ? "float" : null})
    | ^(DIV a=wyrazenie b=wyrazenie) {$typ=najwiekszy($a.typ,$b.typ);} -> div(wyr1={$a.st},wyr2={$b.st},float={$a.typ==2 && $b.typ==2 ? "float" : null})
    | ^(MOD a=wyrazenie b=wyrazenie) {$typ=najwiekszy($a.typ,$b.typ);} -> mod(wyr1={$a.st},wyr2={$b.st})
    | ^(AND a=wyrazenie b=wyrazenie) {$typ=1;} -> and(wyr1={$a.st},wyr2={$b.st})
    | ^(NOT a=wyrazenie) {$typ=1;} -> not(wyr1={$a.st})
    | zmienna {$typ=$zmienna.typ; } -> przekaz(item={$zmienna.st})
    | nazewnik_funkcji {$typ=$nazewnik_funkcji.typ; }-> przekaz(item={$nazewnik_funkcji.st})
    | NUM_INT {$typ=1;} -> num(val={$NUM_INT.text}) //przekaz(item={$NUM_INT.text})
    | NUM_REAL {$typ=2; }-> num(val={$NUM_REAL.text}) //przekaz(item={$NUM_REAL.text})
    | napis {$typ=3;} -> przekaz(item={$napis.st})
    | NIL {$typ=0;} -> przekaz(item={$NIL.text})
    ;

nazewnik_funkcji returns [int typ]
    : ^(FUNC_CALL IDENT lista_parametrow_aktualnych?)  {
	if ( !isDefined(new Symbol($IDENT.text, "funkcja") ) ) {
		System.err.println("niezdefiniowana funkcja: "+$IDENT.text);		
		System.exit(1);
	}
	Symbol s=getSymbol($IDENT.text, "funkcja");
	$typ=s.retval;
    } -> wywolanieFunkcji(id={$IDENT.text}, parametry={$lista_parametrow_aktualnych.st},classname={programName})
    ;

lista_parametrow_aktualnych returns [List<String> paramTypes, List<Integer> paramTypesInt]
@init {
	$paramTypes=new LinkedList<String>();
	$paramTypesInt=new LinkedList<Integer>();
}
    : ^( ARGLIST p+=parametr_aktualny+ {
    		int typ=$parametr_aktualny.typ;
    		String typeDescr;
    		if(typ==1)
	    		typeDescr="I";
		else if(typ==2)
	    		typeDescr="F";
		else
	    		typeDescr="Ljava/lang/String;";
	    	$paramTypes.add(typeDescr);
	    	$paramTypesInt.add(typ);
    	} ) 
    	-> nlIdList(ids={$p})
    ;

element
    : ^(DWIEKROPKI wyrazenie wyrazenie)
    | wyrazenie
    ;

instrukcja_procedury
    : ^(PROC_CALL IDENT lista_parametrow_aktualnych?)  {
	if ( !isDefined(new Symbol($IDENT.text, "procedura") ) ) {
		System.err.println("niezdefiniowana procedura: "+$IDENT.text);
		System.exit(1);
	}
    } -> wywolanieProcedury(id={$IDENT.text}, 
    	parametry={$lista_parametrow_aktualnych.st},
    	classname={programName},
    	writeln={$IDENT.text.toLowerCase().equals("writeln") ? "writeln" : null}, 
    	writelnstr={$lista_parametrow_aktualnych.paramTypesInt.size()>0 && $lista_parametrow_aktualnych.paramTypesInt.get(0)==3 ? "str" : null}, 
    	writelnfloat={$lista_parametrow_aktualnych.paramTypesInt.size()>0 && $lista_parametrow_aktualnych.paramTypesInt.get(0)==2 ? "float" : null},
    	typy={$lista_parametrow_aktualnych.paramTypes})
    ;

parametr_aktualny returns [int typ]
    : wyrazenie {
    	$typ=$wyrazenie.typ;
    }
    -> przekaz(item={$wyrazenie.st})
    ;

instrukcja_skoku
    : ^(GOTO etykieta) -> skok(et={$etykieta.st})
    ;

instrukcja_strukturalna
    : instrukcja_zlozona -> przekaz(item={$instrukcja_zlozona.st})
    | instrukcja_warunkowa -> przekaz(item={$instrukcja_warunkowa.st})
    | instrukcja_iteracyjna -> przekaz(item={$instrukcja_iteracyjna.st})
    | instrukcja_wiazaca -> przekaz(item={$instrukcja_wiazaca.st})
    ;

instrukcja_zlozona returns [int liczbaInstrukcji]
    : ciag_instrukcji {$liczbaInstrukcji=$ciag_instrukcji.count; } -> przekaz(item={$ciag_instrukcji.st})
    ;

ciag_instrukcji returns [int count]
    : ^(BLOCK a+=instrukcja*) { $count=$a.size(); }-> ciagInstrukcji(instrs={$a})
    ;

instrukcja_warunkowa
    : instrukcja_jesli -> przekaz(item={$instrukcja_jesli.st})
    | instrukcja_wyboru -> przekaz(item={$instrukcja_wyboru.st})
    ;

instrukcja_jesli
    : ^(IF a=wyrazenie b=instrukcja c=instrukcja?) -> if(wyr={$a.st}, instr1={$b.st}, instr2={$c.st},num={$PScope::ifCount++})
    ;

instrukcja_wyboru 
    : ^(CASE a=wyrazenie
        b+=element_listy_wyboru+
        c=ciag_instrukcji?
       ) -> case(wyr={$a.st},cases={$b},elsecase={$c.st}, casenum={$PScope::caseCount++})
    ;

element_listy_wyboru
    : ^(DWUKROPEK lista_stalych_wyboru instrukcja) -> elementListyWyboru(consts={$lista_stalych_wyboru.st},instr={$instrukcja.st})
    ;

instrukcja_iteracyjna
    : instrukcja_dopoki -> przekaz(item={$instrukcja_dopoki.st})
    | instrukcja_powtarzaj -> przekaz(item={$instrukcja_powtarzaj.st})
    | instrukcja_dla -> przekaz(item={$instrukcja_dla.st})
    ;

instrukcja_dopoki
    : ^(WHILE wyrazenie instrukcja) -> while(wyr={$wyrazenie.st},instr={$instrukcja.st},num={$PScope::whileCount++})
    ;

instrukcja_powtarzaj
    : ^(REPEAT ciag_instrukcji wyrazenie) -> repeat(wyr={$wyrazenie.st}, instr={$ciag_instrukcji.st},num={$PScope::repeatCount++})
    ;

instrukcja_dla
    : ^(FOR IDENT a=sekwencja_for[getSymbol($IDENT.text,"zmienna").count,$PScope::forCount] instrukcja) 
   // ( 
   // {$a.to}? 
    -> for(to={ $a.to ? "to" : null }, id={$IDENT.text}, seq={$a.st}, instr={$instrukcja.st}, num={$PScope::forCount++}, start={$a.start}, end={$a.end}, varnum={getSymbol($IDENT.text,"zmienna").count})
  //  | 
  //  {!$a.to}? 
    //-> forDownto(id={$IDENT.text}, seq={$a.st}, instr={$instrukcja.st}, num={$PScope::forCount++}, start={a.start}, end={$a.end}, varnum={getSymbol($IDENT.text,"zmienna").count}) 
  //  )
    ;

sekwencja_for [int startnum,int fornum] returns [boolean to, Object start, Object end]
    : ^(TO wartosc_poczatkowa wartosc_koncowa) { $to=true; $start=$wartosc_poczatkowa.st; $end=$wartosc_koncowa.st; } -> sekwencjaForTo(startnum={$startnum},fornum={$fornum},end={$wartosc_koncowa.st})
    | ^(DOWNTO wartosc_poczatkowa wartosc_koncowa) { $to=false; $start=$wartosc_poczatkowa.st; $end=$wartosc_koncowa.st; } -> sekwencjaForDownto(startnum={$startnum},fornum={$fornum},end={$wartosc_koncowa.st})
    ;

wartosc_poczatkowa
    : wyrazenie -> przekaz(item={$wyrazenie.st})
    ;

wartosc_koncowa
    : wyrazenie -> przekaz(item={$wyrazenie.st})
    ;

instrukcja_wiazaca
    : ^(WITH a=lista_zmiennych_rekordowych b=instrukcja) -> instrukcjaWiazaca(rekordy={$a.st},instr={$b.st})
    ;

lista_zmiennych_rekordowych
    : a+=zmienna+ -> commaIdlist(ids={$a})
    ;
